/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2019年1月7日
 */

package com.massyframework.beanskin.spring.beancontext;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;

import com.massyframework.beanskin.assembly.beans.BeanContextAdapter;
import com.massyframework.beanskin.assembly.beans.BeanNotFoundException;

import java.lang.annotation.Annotation;
import java.util.*;

/**
 * {@link SpringBeanContextAdapter} 实现Spring容器的Adapter.
 */
public final class SpringBeanContextAdapter implements BeanContextAdapter {

    private final ConfigurableApplicationContext applicationContext;

    /**
     * 构造方法
     * @param applicationContext {@link ConfigurableApplicationContext}, Spring应用程序上下文
     */
    public SpringBeanContextAdapter(ConfigurableApplicationContext applicationContext) {
        this.applicationContext = Objects.requireNonNull(
                applicationContext, "\"applicationContext\" cannot be null.");
    }

    /**
     * 判断<code>beanName</code>的Bean是否存在
     *
     * @param beanName {@link String}, bean名称
     * @return {@link boolean}, 返回<code>true</code>表示存在，返回<code>false</code>表示不存在。
     */
    @Override
    public boolean containsBean(String beanName) {
        if (beanName == null) return false;
        return this.getApplicationContext().containsBean(beanName);
    }

    /**
     * 根据<code>beanName</code>获取Bean实例
     *
     * @param beanName {@link String}, bean名称
     * @return {@link Object}
     * @throws BeanNotFoundException 未找到Bean时抛出例外
     */
    @Override
    public Object getBean(String beanName) throws BeanNotFoundException {
        try {
            return this.getApplicationContext().getBean(beanName);
        }catch(Exception e) {
        	throw new BeanNotFoundException(beanName, e);
        }
    }
    
    

    @Override
	public Class<?> getBeanType(String beanName) throws BeanNotFoundException {
    	ConfigurableListableBeanFactory beanFactory = this.getApplicationContext().getBeanFactory();
    	BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
        if (definition != null){
        	String className = definition.getBeanClassName();
            ClassLoader classLoader = this.getApplicationContext().getClassLoader();

            try {
                return classLoader.loadClass(className);
            }catch(Exception e) {
            	return null;
            }
        }else {
        	throw new BeanNotFoundException(beanName);
        }
	}

	/**
     * 获取所有派生于<code>beanType</code>的实例<br>
     *
     * @param beanType {@link Class}, bean实现的类型
     * @return {@link Map}
     */
    @Override
    public <B> Map<String, B> getBeansOfType(Class<B> beanType) {
        return this.getApplicationContext().getBeansOfType(beanType);
    }

    /**
     * 获取所有在类上注解<code>annoType</code>的bean实例
     *
     * @param annoType {@link Class}.注解类型。
     * @return {@link Map}
     */
    @Override
    public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annoType) {
        return this.getApplicationContext().getBeansWithAnnotation(annoType);
    }

    
    @Override
	public List<String> getBeanNames() {
    	List<String> result = new ArrayList<>();
    	String[] beanNames = this.applicationContext.getBeanDefinitionNames();
    	for (String beanName: beanNames) {
    		result.add(beanName);
    	}
    	return result;
	}

	/**
     * 取回类型实现的所有接口
     * @param type {@link Class}
     * @param interfaceTypes {@link Set}
     */
    protected void retrieveInterfaceTypes(Class<?> type, Set<Class<?>> interfaceTypes){
        if (type == Object.class) return;

        Class<?>[] types = type.getInterfaces();
        for (Class<?> clazz: types){
            interfaceTypes.add(clazz);
        }

        this.retrieveInterfaceTypes(type.getSuperclass(), interfaceTypes);
    }

    /**
     * {@link ConfigurableApplicationContext}
     * @return {@link ConfigurableApplicationContext}
     */
    public ConfigurableApplicationContext getApplicationContext() {
        return this.applicationContext;
    }
}
